///////////////////////////////////////////////////////////////////////////////
// File:        SRP6.cc
// Author:      671643387@qq.com
// Description:
///////////////////////////////////////////////////////////////////////////////

#include <cassert>
#include "auth/SRP6.hpp"

///////////////////////////////////////////////////////////////////////////////

SRP6::SRP6(void)
{
    N_.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
    g_.SetUInt32(7);
}

SRP6::~SRP6(void)
{

}

///////////////////////////////////////////////////////////////////////////////

void SRP6::SetP(const char* P)
{
    assert(P);
    s_.SetRand(s_BYTE_SIZE * 8);

    BigNumber I;
    I.SetHexStr(P);

    uint8_t digest[SHA_DIGEST_LENGTH] = { 0 };
    if (I.GetNumBytes() <= SHA_DIGEST_LENGTH)
        memcpy(digest, I.AsByteArray(), I.GetNumBytes());
    std::reverse(digest, digest + SHA_DIGEST_LENGTH);

    Sha1Hash sha;
    sha.UpdateData(s_.AsByteArray(), s_.GetNumBytes());
    sha.UpdateData(digest, SHA_DIGEST_LENGTH);
    sha.Finalize();

    BigNumber x;
    x.SetBinary(sha.GetDigest(), sha.GetLength());

    v_ = g_.ModExp(x, N_);
}

void SRP6::SetVS(const char* v, const char* s)
{
    assert(v && s);
    v_.SetHexStr(v);
    s_.SetHexStr(s);
}

///////////////////////////////////////////////////////////////////////////////

void SRP6::DoCalculateStepOne(void)
{
    b_.SetRand(19 * 8);
    BigNumber gmod = g_.ModExp(b_, N_);
    B_ = ((v_ * 3) + gmod) % N_;
    assert(gmod.GetNumBytes() <= 32);
}

void SRP6::DoCalculateStepTwo(BigNumber& A, BigNumber& M, const std::string& I)
{
    Sha1Hash sha;
    sha.UpdateBigNumbers(&A, &B_, NULL);
    sha.Finalize();

    BigNumber u;
    u.SetBinary(sha.GetDigest(), SHA_DIGEST_LENGTH);

    BigNumber S = (A * (v_.ModExp(u, N_))).ModExp(b_, N_);

    uint8_t t[32];
    uint8_t t1[16];
    uint8_t vK[40];
    memcpy(t, S.AsByteArray(32), 32);
    for (int x = 0; x < 16; ++x)
        t1[x] = t[x * 2];

    sha.Initialize();
    sha.UpdateData(t1, 16);
    sha.Finalize();
    for (int x = 0; x < 20; ++x)
        vK[x * 2] = sha.GetDigest()[x];
    for (int x = 0; x < 16; ++x)
        t1[x] = t[x * 2 + 1];

    sha.Initialize();
    sha.UpdateData(t1, 16);
    sha.Finalize();
    for (int x = 0; x < 20; ++x)
        vK[x * 2 + 1] = sha.GetDigest()[x];
    K_.SetBinary(vK, 40);

    uint8_t hash[20];

    sha.Initialize();
    sha.UpdateBigNumbers(&N_, NULL);
    sha.Finalize();
    memcpy(hash, sha.GetDigest(), 20);

    sha.Initialize();
    sha.UpdateBigNumbers(&g_, NULL);
    sha.Finalize();
    for (int x = 0; x < 20; ++x)
        hash[x] ^= sha.GetDigest()[x];

    BigNumber t3;
    t3.SetBinary(hash, 20);

    sha.Initialize();
    sha.UpdateData(I);
    sha.Finalize();
    uint8_t t4[SHA_DIGEST_LENGTH];
    memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);

    sha.Initialize();
    sha.UpdateBigNumbers(&t3, NULL);
    sha.UpdateData(t4, SHA_DIGEST_LENGTH);
    sha.UpdateBigNumbers(&s_, &A, &B_, &K_, NULL);
    sha.Finalize();

    M.SetBinary(sha.GetDigest(), SHA_DIGEST_LENGTH);
}